// -----------------------------------------------------------------------
//  <copyright file="ApiResultWrapperFilterAttribute.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/8 3:02:06</last-date>
// -----------------------------------------------------------------------

using DaprPlus.AspNetCore.Extensions;
using DaprPlus.Domain;

using Microsoft.AspNetCore.Mvc.Filters;


namespace DaprPlus.AspNetCore.Mvc.Filters;

/// <summary>
/// 将API返回结果进行统一封装的过滤器特性
/// </summary>
public class ApiResultWrapperFilterAttribute : ActionFilterAttribute
{
    /// <inheritdoc />
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var provider = context.HttpContext.RequestServices;
        var logger = provider.GetLogger<ApiResultWrapperFilterAttribute>();

        if (context is { Exception: not null, ExceptionHandled: false })
        {
            var ex = context.Exception;
            logger.LogError(new EventId(), ex, ex.Message);

            // 使用依赖注入的异常处理器来处理异常
            var exceptionHandlers = provider.GetServices<IApiResultExceptionHandler>()
                .OrderBy(h => h.Priority)
                .ToList();

            foreach (var handler in exceptionHandlers)
            {
                if (handler.CanHandle(ex) && handler.Handle(ex, context))
                {
                    return; // 异常已被处理
                }
            }
        }

        // 处理正常返回结果
        HandleNormalResult(context);
    }

    /// <summary>
    /// 处理正常返回结果
    /// </summary>
    /// <param name="context">Action执行上下文</param>
    private static void HandleNormalResult(ActionExecutedContext context)
    {
        switch (context.Result)
        {
            case StatusCodeResult statusCodeResult when context.Result is not ObjectResult { Value: ApiResult }:
                {
                    var apiResult = new ApiResult(GetApiResultType(statusCodeResult.StatusCode));
                    context.Result = new ObjectResult(apiResult)
                    {
                        StatusCode = statusCodeResult.StatusCode
                    };
                    break;
                }
            case JsonResult { Value: not ApiResult } jsonResult:
                {
                    var apiResult = jsonResult.Value == null
                        ? new ApiResult(ApiResultType.Success)
                        : new ApiResult(jsonResult.Value);
                    context.Result = new ObjectResult(apiResult) { StatusCode = jsonResult.StatusCode };
                    break;
                }
            case OkObjectResult { Value: not ApiResult } okObjectResult:
                {
                    ApiResult apiResult;
                    if (okObjectResult.Value is string msg)
                    {
                        apiResult = new ApiResult(msg);
                    }
                    else
                    {
                        apiResult = new ApiResult(okObjectResult.Value!);
                    }

                    context.Result = new ObjectResult(apiResult) { StatusCode = okObjectResult.StatusCode };
                    break;
                }
            case ObjectResult { Value: not ApiResult } objectResult:
                {
                    if (objectResult.DeclaredType?.IsSimpleType() == true)
                    {
                        return;
                    }
                    var apiResult = objectResult.Value == null
                        ? new ApiResult(ApiResultType.Success)
                        : new ApiResult(objectResult.Value);
                    context.Result = new ObjectResult(apiResult) { StatusCode = objectResult.StatusCode, DeclaredType = typeof(ApiResult) };
                    break;
                }
        }
    }

    /// <summary>
    /// 根据HTTP状态码获取ApiResult类型
    /// </summary>
    /// <param name="statusCode">HTTP状态码</param>
    /// <returns>ApiResult类型</returns>
    private static ApiResultType GetApiResultType(int statusCode)
    {
        return statusCode switch
        {
            200 => ApiResultType.Success,
            203 => ApiResultType.Info,
            401 => ApiResultType.UnAuth,
            403 => ApiResultType.Forbidden,
            404 => ApiResultType.NotFound,
            423 => ApiResultType.Locked,
            _ => ApiResultType.Error
        };
    }
}